home *** CD-ROM | disk | FTP | other *** search
- Subject: v21i087: Safely rename wildcarded files, Part01/02
- Newsgroups: comp.sources.unix
- Approved: rsalz@uunet.UU.NET
- X-Checksum-Snefru: 179451ca bae97e21 ebca953d 10feaec3
-
- Submitted-by: Vladimir Lanin <lanin@csd4.cs.nyu.edu>
- Posting-number: Volume 21, Issue 87
- Archive-name: mmv/part01
-
- [ I use ren, the predecessor to mmv, all the time. This is even
- better! --r$ ]
-
- This is mmv, a program to move/copy/append/link multiple files according
- to a set of wildcard patterns. This multiple action is performed safely,
- i.e., without any unexpected deletion of files due to collisions of target
- names with existing filenames or with other target names. Furthermore,
- before doing anything, mmv attempts to detect any errors that would result
- from the entire set of actions specified and gives the user the choice of
- either aborting before beginning, or proceeding by avoiding the offending
- parts.
-
- Improvements over mmv's predecessor, ren:
- Support for BSD, System 5, and MS-DOS
- Source and target files may (usually) reside in different directories
- Paths may contain wildcards
- Supports all csh wildcards: '*', '?', '['...']', and '~'
- The ';' wildcard finds files at any level in the tree
- Can copy, append, or link instead of moving/renaming
- Reads multiple patterns from standard input (or one from command line)
- No-execute option (whose output can be fed back in on standard input)
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 1 (of 2)."
- # Contents: MANIFEST Makefile PACKNOTES announce mmv.1 mmv.c.2
- # Wrapped by rsalz@litchi.bbn.com on Mon Apr 9 17:05:22 1990
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'MANIFEST' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'MANIFEST'\"
- else
- echo shar: Extracting \"'MANIFEST'\" \(359 characters\)
- sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
- X File Name Archive # Description
- X-----------------------------------------------------------
- X MANIFEST 1
- X Makefile 1
- X PACKNOTES 1 Warnings about long lines, etc
- X announce 1
- X mmv.1 1
- X mmv.c.1 2 (part 1)
- X mmv.c.2 1 (part 2)
- END_OF_FILE
- if test 359 -ne `wc -c <'MANIFEST'`; then
- echo shar: \"'MANIFEST'\" unpacked with wrong size!
- fi
- # end of 'MANIFEST'
- fi
- if test -f 'Makefile' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Makefile'\"
- else
- echo shar: Extracting \"'Makefile'\" \(281 characters\)
- sed "s/^X//" >'Makefile' <<'END_OF_FILE'
- Xall: mmv mmv.1
- X
- Xmmv: mmv.c
- X $(CC) -o mmv $(CFLAGS) mmv.c
- Xinstall: all
- X @echo "Install mmv according to local convention,"
- X @echo "then make links named mcp, mad, and mln to mmv."
- X @echo "Under System V, edit mmv.1 to uncomment the .nr O 1 line."
- Xclean:
- X rm -f core a.out mmv mmv.o
- END_OF_FILE
- if test 281 -ne `wc -c <'Makefile'`; then
- echo shar: \"'Makefile'\" unpacked with wrong size!
- fi
- # end of 'Makefile'
- fi
- if test -f 'PACKNOTES' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'PACKNOTES'\"
- else
- echo shar: Extracting \"'PACKNOTES'\" \(82 characters\)
- sed "s/^X//" >'PACKNOTES' <<'END_OF_FILE'
- X
- XFile "mmv.c" was split because of its size; to create it, do
- X cat mmv.c.? >mmv.c
- END_OF_FILE
- if test 82 -ne `wc -c <'PACKNOTES'`; then
- echo shar: \"'PACKNOTES'\" unpacked with wrong size!
- fi
- # end of 'PACKNOTES'
- fi
- if test -f 'announce' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'announce'\"
- else
- echo shar: Extracting \"'announce'\" \(1588 characters\)
- sed "s/^X//" >'announce' <<'END_OF_FILE'
- XCopyright (c) 1989 Vladimir Lanin
- X
- XThis is mmv, a program to move/copy/append/link multiple files
- Xaccording to a set of wildcard patterns. This multiple action is
- Xperformed safely, i.e. without any unexpected deletion of files due to
- Xcollisions of target names with existing filenames or with other
- Xtarget names. Furthermore, before doing anything, mmv attempts to
- Xdetect any errors that would result from the entire set of actions
- Xspecified and gives the user the choice of either aborting before
- Xbeginning, or proceeding by avoiding the offending parts.
- X
- XImprovements over mmv's predecessor, ren:
- X
- X. support for BSD, System 5, and MS-DOS
- X
- X. source and target files may (usually) reside in different directories
- X
- X. paths may contain wildcards
- X
- X. supports all csh wildcards: '*', '?', '['...']', and '~'
- X
- X. the ';' wildcard finds files at any level in the tree
- X
- X. can copy, append, or link instead of moving/renaming
- X
- X. reads multiple patterns from standard input (or one from command line)
- X
- X. no-execute option (whose output can be fed back in on standard input)
- X
- XNote to users familiar with ren: the -a and -k options have been renamed
- Xto -t and -g, respectively, and their semantics have somewhat changed.
- X
- X
- XMmv is freeware. That means that the entire package of software and
- Xdocumentation is copyrighted, and may not be distributed with any
- Xmodifications or for any charge (without the author's explicit written
- Xpermission). Other than that, it may be used and distributed freely.
- X
- X
- XVladimir Lanin
- X330 Wadsworth Ave, Apt 6F
- XNew York, NY 10040
- X
- Xlanin@csd2.nyu.edu
- X...!cmcl2!csd2!lanin
- END_OF_FILE
- if test 1588 -ne `wc -c <'announce'`; then
- echo shar: \"'announce'\" unpacked with wrong size!
- fi
- # end of 'announce'
- fi
- if test -f 'mmv.1' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'mmv.1'\"
- else
- echo shar: Extracting \"'mmv.1'\" \(17091 characters\)
- sed "s/^X//" >'mmv.1' <<'END_OF_FILE'
- X.\" Under BSD, just give to nroff or troff (with -man).
- X.\" To print the MS-DOS version, use option -rO2.
- X.\" Under System V, take out the '.\" ' from the next line.
- X.\" .nr O 1
- X.TH MMV 1 "November 20, 1989 (v1.0)"
- X.ie !'\nO'2' \{\
- X.SH NAME
- Xmmv \- move/copy/append/link multiple files by wildcard patterns
- X\}
- X.el \{
- X.SH NAME
- Xmmv \- move/copy/append multiple files by wildcard patterns
- X\}
- X.ie '\nO'2' \{\
- X.ds SL \\\\
- X.ds ES '
- X\}
- X.el \{\
- X.ds SL /
- X.ds ES \\\\
- X\}
- X.SH SYNOPSIS
- X.B mmv
- X.if '\nO'2' [\fB-m\fP|\fBx\fP|\fBr\fP|\fBc\fP|\fBo\fP|\fBa\fP|\fBz\fP]
- X.if '\nO'0' [\fB-m\fP|\fBx\fP|\fBr\fP|\fBc\fP|\fBo\fP|\fBa\fP|\fBl\fP|\fBs\fP]
- X.if '\nO'1' [\fB-m\fP|\fBx\fP|\fBr\fP|\fBc\fP|\fBo\fP|\fBa\fP|\fBl\fP]
- X[\fB-h\fP]
- X[\fB-d\fP|\fBp\fP]
- X[\fB-g\fP|\fBt\fP]
- X[\fB-v\fP|\fBn\fP]
- X[\fBfrom to\fP]
- X.if '\nO'2' \{\
- X.br
- X.B mmvpatch
- X[\fBexecutable\fP]
- X\}
- X.SH "DESCRIPTION"
- X.I Mmv
- Xmoves (or copies,
- X.ie '\nO'2' or appends,
- X.el appends, or links,
- Xas specified)
- Xeach source file matching a
- X.I from
- Xpattern to the target name specified by the
- X.I to
- Xpattern.
- XThis multiple action is performed safely,
- Xi.e. without any unexpected deletion of files
- Xdue to collisions of target names with existing filenames
- Xor with other target names.
- XFurthermore, before doing anything,
- X.I mmv
- Xattempts to detect any errors that would result
- Xfrom the entire set of actions specified
- Xand gives the user the choice of either
- Xproceeding by avoiding the offending parts
- Xor aborting.
- X
- X.ce
- XThe Task Options
- X.PP
- XWhether
- X.I mmv
- Xmoves, copies,
- X.ie '\nO'2' or appends
- X.el appends, or links
- Xis governed by the first set of options given
- Xabove.
- XIf none of these are specified,
- X.ie '\nO'2' \{\
- Xa default (patchable by
- X.IR mmvpatch ,
- Xand initially -x)
- Xdetermines the task.
- X\}
- X.el \{\
- Xthe task is given by the command name under which
- X.I mmv
- Xwas invoked (argv[0]):
- X
- X command name default task
- X
- X mmv -x
- X.br
- X mcp -c
- X.br
- X mad -a
- X.br
- X mln -l
- X\}
- X.PP
- XThe task option choices are:
- X.TP
- X-m :
- Xmove source file to target name.
- XBoth must be on the same device.
- XWill not move directories.
- X.if '\nO'0' \{\
- XIf the source file is a symbolic link,
- Xmoves the link without checking if the link's target from the new
- Xdirectory is different than the old.
- X\}
- X.TP
- X-x :
- Xsame as -m, except cross-device moves are done
- Xby copying, then deleting source.
- XWhen copying, sets the
- X.ie !'\nO'2' permission bits
- X.el attributes
- Xand file modification time
- Xof the target file to that of the source file.
- X.TP
- X-r :
- Xrename source file or directory to target name.
- XThe target name must not include a path:
- Xthe file remains in the same directory in all cases.
- XThis option is the only way of renaming directories under
- X.IR mmv .
- X.if '\nO'2' It is only available under DOS version 3.0 or higher.
- X.TP
- X-c :
- Xcopy source file to target name.
- XSets the file modification time and
- X.ie !'\nO'2' permission bits
- X.el attributes
- Xof the target file to that of the source file,
- Xregardless of whether the target file already exists.
- XChains and cycles (to be explained below) are not allowed.
- X.TP
- X-o :
- Xoverwrite target name with source file.
- X.ie '\nO'2' \{\
- XIf target file exists, its attributes are left unchanged.
- XIf not, it is created with ordinary attributes
- Xunrelated to the source file's attributes.
- XIn either case, the file modification time is set to the current time.
- X\}
- X.el \{\
- XIf target file exists, it is overwritten,
- Xkeeping its original owner and permission bits.
- XIf it does not exist, it is created, with read-write permission bits
- Xset according to
- X.IR umask (1),
- Xand the execute permission bits copied from the source file.
- XIn either case, the file modification time is set to the current time.
- X\}
- X.TP
- X-a :
- Xappend contents of source file to target name.
- XTarget file modification time is set to the current time.
- XIf target file does not exist,
- Xit is created with
- X.ie '\nO'2' attributes
- X.el permission bits
- Xset as under -o.
- XUnlike all other options, -a allows multiple source files to have the
- Xsame target name, e.g. "mmv -a
- X.ie '\nO'2' *.c
- X.el \\*.c
- Xbig" will append all ".c" files to "big".
- XChains and cycles are also allowed, so "mmv -a f f" will double up "f".
- X.ie '\nO'2' \{\
- X.TP
- X-z :
- Xsame as -a, but if the target file exists, and its last character is a ^Z,
- Xand the source file is not empty,
- Xthis ^Z is truncated before doing the append.
- X\}
- X.el \{\
- X.TP
- X-l :
- Xlink target name to source file.
- XBoth must be on the same device,
- Xand the source must not be a directory.
- XChains and cycles are not allowed.
- X.if '\nO'0' \{\
- X.TP
- X-s :
- Xsame as -l, but use symbolic links instead of hard links.
- XFor the resulting link to aim back at the source,
- Xeither the source name must begin with a '/',
- Xor the target must reside in either the current or the source directory.
- XIf none of these conditions are met, the link is refused.
- XHowever, source and target can reside on different devices,
- Xand the source can be a directory.
- X\}
- X\}
- X.PP
- XOnly one of these option may be given,
- Xand it applies to all matching files.
- XRemaining options need not be given separately,
- Xi.e. "mmv -mk" is allowed.
- X
- X.ce
- XMultiple Pattern Pairs
- X.PP
- XMultiple
- X.I from
- X--
- X.I to
- Xpattern pairs may be specified by omitting
- Xthe pattern pair on the command line,
- Xand entering them on the standard input,
- Xone pair per line.
- X(If a pattern pair is given on the command line,
- Xthe standard input is not read.)
- XThus,
- X
- X.in +3
- Xmmv
- X.br
- Xa b
- X.br
- Xc d
- X.in -3
- X
- Xwould rename "a" to "b" and "c" to "d".
- XIf a file can be matched to several of the given
- X.I from
- Xpatterns,
- Xthe
- X.I to
- Xpattern of the first matching pair is used.
- XThus,
- X
- X.in +3
- Xmmv
- X.br
- Xa b
- X.br
- Xa c
- X.in -3
- X
- Xwould give the error message "a -> c : no match" because file "a"
- X(even if it exists)
- Xwas already matched by the first pattern pair.
- X
- X.ce
- XThe \fIFrom\fP Pattern
- X.PP
- XThe
- X.I from
- Xpattern is a filename
- Xwith embedded wildcards: '*', '?', '['...']',
- X.if '\nO'2' \{\
- X\'!',
- X\}
- Xand ';'.
- XThe first three have their usual
- X.IR sh (1)
- Xmeanings of, respectively,
- Xmatching any string of characters,
- Xmatching any single character,
- Xand matching any one of a set of characters.
- X.PP
- XBetween the '[' and ']', a range from character 'a' through character 'z'
- Xis specified with "a-z".
- XThe set of matching characters can be negated by inserting
- Xa '^' after the '['.
- XThus, "[^b-e2-5_]"
- Xwill match any character but 'b' through 'e', '2' through '5', and '_'.
- X.if '\nO'2' \{\
- X.PP
- XUnlike DOS wildcards,
- Xall mmv wildcards (except for cases listed below)
- Xcan occur anywhere in the pattern,
- Xwhether preceding or following explicit characters or other wildcards.
- XFor example, the pattern "*z\\foo.bar" will search
- Xfor files named "foo.bar" in all subdirectories whose names end in 'z'.
- XHowever, no wildcards can occur in the drive letter.
- X.PP
- XThe character '.' is not matched by any of '*', '?', or '['...']'.
- XThus, the pattern "*" will only match files with a null extension.
- XTo save yourself some typing, use the '!' wildcard instead,
- Xwhich matches the same as "*.*",
- Xexcept it is assigned only one wildcard index (see below).
- XThus, both "f!" and "f*.*"
- Xwill match all of "f", "f.ext", "foo", and "foo.ext",
- Xwhile "f*" will match only the first and the third.
- X\}
- X.PP
- XNote that paths are allowed in the patterns,
- Xand wildcards may be intermingled with slashes arbitrarily.
- XThe ';' wildcard
- Xis useful for matching files at any depth in the directory tree.
- XIt matches the same as "*\*(SL" repeated any number of times, including zero,
- Xand can only occur either at the beginning of the pattern
- Xor following a '\*(SL'.
- XThus ";*.c" will match all ".c" files in or below the current directory,
- Xwhile "\*(SL;*.c" will match them anywhere on the file system.
- X.if !'\nO'2' \{\
- X.PP
- XIn addition, if the
- X.I from
- Xpattern
- X(or the
- X.I to
- Xpattern)
- Xbegins with "~/", the '~' is replaced with the home directory name.
- X(Note that the "~user" feature of
- X.IR csh (1)
- Xis not implemented.)
- XHowever, the '~' is not treated as a wildcard,
- Xin the sense that it is not assigned a wildcard index (see below).
- X\}
- X.PP
- XSince matching a directory under a task option other than -r or -s
- Xwould result in an error,
- Xtasks other than -r and -s
- Xmatch directories only against completely explicit
- X.I from
- Xpatterns (i.e. not containing wildcards).
- XUnder -r and -s, this applies only to "." and "..".
- X.PP
- X.ie '\nO'2' \{\
- XHidden and system files are also only matched
- Xagainst completely explicit
- X.I from
- Xpatterns.
- X\}
- X.el \{\
- XFiles beginning with '.' are only matched against
- X.I from
- Xpatterns that begin with an explicit '.'.
- X\}
- XHowever, if -h is specified, they are matched normally.
- X.if !'\nO'2' \{\
- X.PP
- XWarning: since the shell normally expands wildcards
- Xbefore passing the command-line arguments to
- X.IR mmv ,
- Xit is usually necessary to enclose the command-line
- X.I from
- Xpattern
- Xin quotes.
- X\}
- X
- X.ce
- XThe \fITo\fP Pattern
- X.PP
- XThe
- X.I to
- Xpattern is a filename
- Xwith embedded
- X.I wildcard
- X.IR indexes ,
- Xwhere an index consists of the character '='
- Xfollowed by a string of digits.
- XWhen a source file matches a
- X.I from
- Xpattern,
- Xa target name for the file is constructed out of the
- X.I to
- Xpattern by
- Xreplacing the wildcard indexes by the
- Xactual characters that matched the referenced wildcards
- Xin the source name.
- XThus, if the
- X.I from
- Xpattern is "abc*.*" and the
- X.I to
- Xpattern is "xyz=2.=1",
- Xthen "abc.txt" is targeted to "xyztxt.".
- X(The first '*' matched "", and the second matched "txt".)
- XSimilarly, for the pattern pair ";*.[clp]" -> "=1=3\*(SL=2",
- X"foo1\*(SLfoo2\*(SLprog.c" is targeted to "foo1\*(SLfoo2\*(SLc\*(SLprog".
- XNote that there is no '\*(SL' following the "=1" in the
- X.I to
- Xpattern,
- Xsince the string matched by any ';' is always either empty
- Xor ends in a '\*(SL'.
- XIn this case, it matches "foo1\*(SLfoo2\*(SL".
- X.if !'\nO'2' \{\
- X.PP
- XTo convert the string matched by a wildcard
- Xto either lowercase or uppercase before embedding it in the target name,
- Xinsert 'l' or 'u', respectively,
- Xbetween the '=' and the string of digits.
- X.PP
- XThe
- X.I to
- Xpattern,
- Xlike the
- X.I from
- Xpattern,
- Xcan begin with a "~/" (see above).
- XThis does not necessitate enclosing the
- X.I to
- Xpattern in quotes on the command line
- Xsince
- X.IR csh (1)
- Xexpands the '~' in the exact same manner as
- X.I mmv
- X(or, in the case of
- X.IR sh (1),
- Xdoes not expand it at all).
- X\}
- X.PP
- XFor all task options other than -r, if the target name is a directory,
- Xthe real target name is formed by appending
- Xa '\*(SL' followed by the last component
- Xof the source file name.
- XFor example, "mmv dir1\*(SLa dir2" will,
- Xif "dir2" is indeed a directory, actually move "dir1\*(SLa" to "dir2\*(SLa".
- XHowever, if "dir2\*(SLa" already exists and is itself a directory,
- Xthis is considered an error.
- X.PP
- XTo strip any character (e.g. '*', '?', or '=')
- Xof its special meaning to
- X.IR mmv ,
- Xas when the actual replacement name must contain the character '=',
- Xprecede the special character with a
- X.ie '\nO'2' \{\
- Xsingle quote (').
- X\}
- X.el \{\
- X\'\\'
- X(and enclose the argument in quotes because of the shell).
- X\}
- XThis also works to terminate a wildcard index
- Xwhen it has to be followed by a digit in the filename, e.g. "a=1\*(ES1".
- X
- X.ce
- XChains and Cycles
- X.PP
- XA chain is a sequence of specified actions where the target name of
- Xone action refers to the source file of another action.
- XFor example,
- X
- Xmmv
- X.br
- Xa b
- X.br
- Xb c
- X
- Xspecifies the chain "a" -> "b" -> "c".
- XA cycle is a chain where the last target name
- Xrefers back to the first source file,
- Xe.g. "mmv a a".
- X.I Mmv
- Xdetects chains and cycles regardless of the order in which
- Xtheir constituent actions are actually given.
- XWhere allowed, i.e. in moving, renaming, and appending files,
- Xchains and cycles are handled gracefully, by performing them in the proper
- Xorder.
- XCycles are broken by first renaming one of the files to a temporary name
- X(or just remembering its original size when doing appends).
- X
- X.ce
- XCollisions and Deletions
- X.PP
- XWhen any two or more matching files
- Xwould have to be
- X.ie '\nO'2' moved or copied
- X.el moved, copied, or linked
- Xto the same target filename,
- X.I mmv
- Xdetects the condition as an error before performing any actions.
- XFurthermore,
- X.I mmv
- Xchecks if any of its actions will result
- Xin the destruction of existing files.
- XIf the -d (delete) option is specified,
- Xall file deletions or overwrites are done silently.
- XUnder -p (protect), all deletions or overwrites
- X(except those specified with "(*)" on the standard input, see below)
- Xare treated as errors.
- XAnd if neither option is specified,
- Xthe user is queried about each deletion or overwrite separately.
- X(A new stream to
- X.ie '\nO'2' "\\dev\\con"
- X.el "/dev/tty"
- Xis used for all interactive queries,
- Xnot the standard input.)
- X
- X.ce
- XError Handling
- X.PP
- XWhenever any error in the user's action specifications is detected,
- Xan error message is given on the standard output,
- Xand
- X.I mmv
- Xproceeds to check the rest of the specified actions.
- XOnce all errors are detected,
- X.I mmv
- Xqueries the user whether he wishes
- Xto continue by avoiding the erroneous actions or to abort altogether.
- XThis and all other queries may be avoided by specifying either the
- X-g (go) or -t (terminate) option.
- XThe former will resolve all difficulties by avoiding the erroneous actions;
- Xthe latter will abort
- X.I mmv
- Xif any errors are detected.
- XSpecifying either of them defaults
- X.I mmv
- Xto -p, unless -d is specified
- X(see above).
- XThus, -g and -t are most useful when running
- X.I mmv
- Xin the background or in
- Xa shell script,
- Xwhen interactive queries are undesirable.
- X
- X.ce
- XReports
- X.PP
- XOnce the actions to be performed are determined,
- X.I mmv
- Xperforms them silently,
- Xunless either the -v (verbose) or -n (no-execute) option is specified.
- XThe former causes
- X.I mmv
- Xto report each performed action
- Xon the standard output as
- X
- Xa -> b : done.
- X
- XHere, "a" and "b" would be replaced by the source and target names,
- Xrespectively.
- XIf the action deletes the old target,
- Xa "(*)" is inserted after the the target name.
- XAlso, the "->" symbol is modified when a cycle has to be broken:
- Xthe '>' is changed to a '^' on the action prior to which the old target
- Xis renamed to a temporary,
- Xand the '-' is changed to a '=' on the action where the temporary is used.
- X.PP
- XUnder -n, none of the actions are performed,
- Xbut messages like the above are printed on the standard output
- Xwith the ": done." omitted.
- X.PP
- XThe output generated by -n can (after editing, if desired)
- Xbe fed back to
- X.I mmv
- Xon the standard input
- X(by omitting the
- X.I from
- X--
- X.I to
- Xpair on the
- X.I mmv
- Xcommand line).
- XTo facilitate this,
- X.I mmv
- Xignores lines on the standard input that look
- Xlike its own error and "done" messages,
- Xas well as all lines beginning with white space,
- Xand will accept pattern pairs with or without the intervening "->"
- X(or "-^", "=>", or "=^").
- XLines with "(*)" after the target pattern have the effect of enabling -d
- Xfor the files matching this pattern only,
- Xso that such deletions are done silently.
- XWhen feeding
- X.I mmv
- Xits own output,
- Xone must remember to specify again the task option (if any)
- Xoriginally used to generate it.
- X.PP
- XAlthough
- X.I mmv
- Xattempts to predict all mishaps prior to performing any specified actions,
- Xaccidents may happen.
- XFor example,
- X.I mmv
- Xdoes not check for adequate free space when copying.
- XThus, despite all efforts,
- Xit is still possible for an action to fail
- Xafter some others have already been done.
- XTo make recovery as easy as possible,
- X.I mmv
- Xreports which actions have already been done and
- Xwhich are still to be performed
- Xafter such a failure occurs.
- XIt then aborts, not attempting to do anything else.
- XOnce the user has cleared up the problem,
- Xhe can feed this report back to
- X.I mmv
- Xon the standard input
- Xto have it complete the task.
- X(The user is queried for a file name to dump this report
- Xif the standard output has not been redirected.)
- X.if '\nO'2' \{\
- X
- X.ce
- X\fIMmvpatch\fP
- X.PP
- XYou can customize a copy of
- X.I mmv
- Xvia the
- X.I mmvpatch
- Xutility.
- XIf you wish to change the default task option,
- Xrun
- X.I mmvpatch
- Xon a copy of
- X.I mmv
- Xnamed as follows:
- X
- X -x, -m, -r mmv.exe
- X.br
- X -c, -o mcp.exe
- X.br
- X -a, -z mad.exe
- X.PP
- X.I Mmvpatch
- Xalso determines the best way to uniquely identify directories.
- XAs distributed,
- X.I mmv
- Xis set to use a method that is guaranteed to work the same way
- Xfor all versions of DOS,
- Xbut is both slow
- Xand unable to correctly handle drives
- Xaffected by the
- X.I join
- Xand
- X.I subst
- XDOS commands.
- XAlternatively,
- Xthere is a method that is fast and correct,
- Xbut uses an undocumented DOS feature
- Xthat may not work properly under all versions of DOS.
- X(However, 2.0 and 3.3 are known to work.)
- X.I Mmv
- Xdoes
- X.I not
- Xdetermine the best method to use on your system
- Xat run-time since this is too slow.
- XThe choice is left to
- X.I mmvpatch,
- Xwhich determines if the fast method works,
- Xbut also allows you to return to the slow method.
- X\}
- X.SH "EXIT STATUS"
- X.I Mmv
- Xexits with status 1 if it aborts before doing anything,
- Xwith status 2 if it aborts due to failure after completing some of the
- Xactions,
- Xand with status 0 otherwise.
- X.if !'\nO'2' \{\
- X.SH "SEE ALSO"
- Xmv(1), cp(1), ln(1), umask(1)
- X\}
- X.SH "AUTHOR"
- XVladimir Lanin
- X.br
- Xlanin@csd2.nyu.edu
- X.SH "BUGS"
- X.if !'\nO'2' \{\
- XIf the search pattern is not quoted,
- Xthe shell expands the wildcards.
- X.I Mmv
- Xthen (usually) gives some error message,
- Xbut can not determine that the lack of quotes is the cause.
- X.PP
- X\}\
- XTo avoid difficulties in semantics and error checking,
- X.I mmv
- Xrefuses to move or create directories.
- END_OF_FILE
- if test 17091 -ne `wc -c <'mmv.1'`; then
- echo shar: \"'mmv.1'\" unpacked with wrong size!
- fi
- # end of 'mmv.1'
- fi
- if test -f 'mmv.c.2' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'mmv.c.2'\"
- else
- echo shar: Extracting \"'mmv.c.2'\" \(7214 characters\)
- sed "s/^X//" >'mmv.c.2' <<'END_OF_FILE'
- Xstatic int movealias(first, p, pprintaliased)
- X REP *first, *p;
- X int *pprintaliased;
- X{
- X char *fstart;
- X int ret;
- X
- X strcpy(pathbuf, p->r_hto->h_name);
- X fstart = pathbuf + strlen(pathbuf);
- X strcpy(fstart, TEMP);
- X for (
- X ret = 0;
- X sprintf(fstart + STRLEN(TEMP), "%03d", ret),
- X fsearch(fstart, p->r_hto->h_di) != NULL;
- X ret++
- X )
- X ;
- X if (rename(fullrep, pathbuf)) {
- X fprintf(stderr,
- X "%s -> %s has failed.\n", fullrep, pathbuf);
- X *pprintaliased = snap(first, p);
- X }
- X return(ret);
- X}
- X
- X
- Xstatic int snap(first, p)
- X REP *first, *p;
- X{
- X char fname[80];
- X int redirected = 0;
- X
- X if (noex)
- X exit(1);
- X
- X failed = 1;
- X#ifdef MSDOS
- X ctrlbrk((int (*)())breakstat);
- X#else
- X signal(SIGINT, breakstat);
- X#endif
- X if (
- X badstyle == ASKBAD &&
- X isatty(fileno(stdout)) &&
- X getreply("Redirect standard output to file? ", 0)
- X ) {
- X redirected = 1;
- X#ifndef MSDOS
- X umask(oldumask);
- X#endif
- X while (
- X fprintf(stderr, "File name> "),
- X (outfile = fopen(gets(fname), "w")) == NULL
- X )
- X fprintf(stderr, "Can't open %s.\n", fname);
- X }
- X if (redirected || !verbose)
- X showdone(p);
- X fprintf(outfile, "The following left undone:\n");
- X noex = 1;
- X return(first != p);
- X}
- X
- X
- Xstatic void showdone(fin)
- X REP *fin;
- X{
- X REP *first, *p;
- X
- X for (first = hrep.r_next; ; first = first->r_next)
- X for (p = first; p != NULL; p = p->r_thendo) {
- X if (p == fin)
- X return;
- X fprintf(outfile, "%s%s %c%c %s%s : done%s\n",
- X p->r_hfrom->h_name, p->r_ffrom->fi_name,
- X p->r_flags & R_ISALIASED ? '=' : '-',
- X p->r_flags & R_ISCYCLE ? '^' : '>',
- X p->r_hto->h_name, p->r_nto,
- X (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "");
- X }
- X}
- X
- X
- Xstatic void breakout()
- X{
- X fflush(stdout);
- X fprintf(stderr, "Aborting, nothing done.\n");
- X exit(1);
- X}
- X
- X
- Xstatic int breakrep()
- X{
- X gotsig = 1;
- X return(1);
- X}
- X
- X
- Xstatic void breakstat()
- X{
- X exit(1);
- X}
- X
- X
- Xstatic void quit()
- X{
- X fprintf(stderr, "Aborting, nothing done.\n");
- X exit(1);
- X}
- X
- X
- Xstatic int copymove(p)
- X REP *p;
- X{
- X#ifndef MSDOS
- X#ifndef SYSV
- X {
- X int llen;
- X char linkbuf[MAXPATH];
- X
- X if ((llen = readlink(pathbuf, linkbuf, MAXPATH - 1)) != 1) {
- X linkbuf[llen] = '\0';
- X return(symlink(linkbuf, fullrep) || myunlink(pathbuf, p->r_ffrom));
- X }
- X }
- X#endif
- X#endif
- X return(copy(p->r_ffrom, -1) || myunlink(pathbuf, p->r_ffrom));
- X}
- X
- X
- X
- X#define IRWMASK (S_IREAD | S_IWRITE)
- X#define RWMASK (IRWMASK | (IRWMASK >> 3) | (IRWMASK >> 6))
- X
- Xstatic int copy(ff, len)
- X FILEINFO *ff;
- X long len;
- X{
- X char buf[BUFSIZE], c;
- X int f, t, k, mode, perm;
- X#ifdef MSDOS
- X struct ftime tim;
- X#else
- X#ifdef SYSV
- X struct utimbuf tim;
- X#else
- X struct timeval tim[2];
- X#endif
- X struct stat fstat;
- X#endif
- X
- X if ((f = open(pathbuf, O_RDONLY | O_BINARY, 0)) < 0)
- X return(-1);
- X perm =
- X#ifdef MSDOS
- X IRWMASK /* will _chmod it later (to get all the attributes) */
- X#else
- X (op & (APPEND | OVERWRITE)) ?
- X (~oldumask & RWMASK) | (ff->fi_mode & ~RWMASK) :
- X ff->fi_mode
- X#endif
- X ;
- X mode = O_CREAT |
- X#ifdef MSDOS
- X O_BINARY | (op & ZAPPEND ? O_RDWR : O_WRONLY)
- X#else
- X O_WRONLY
- X#endif
- X ;
- X if (!(op & APPEND))
- X mode |= O_TRUNC;
- X if ((t = open(fullrep, mode, perm)) < 0) {
- X close(f);
- X return(-1);
- X }
- X if (op & APPEND)
- X lseek(t, 0, 2);
- X#ifdef MSDOS
- X if (op & ZAPPEND && filelength(t) != 0) {
- X if (lseek(t, -1, 1) == -1L || read(t, &c, 1) != 1) {
- X close(f);
- X close(t);
- X return(-1);
- X }
- X if (c == 26)
- X lseek(t, -1, 1);
- X }
- X#endif
- X if ((op & APPEND) && len != -1L) {
- X while (
- X len != 0 &&
- X (k = read(f, buf, len > BUFSIZE ? BUFSIZE : (unsigned)len)) > 0 &&
- X write(t, buf, k) == k
- X )
- X len -= k;
- X if (len == 0)
- X k = 0;
- X }
- X else
- X while ((k = read(f, buf, BUFSIZE)) > 0 && write(t, buf, k) == k)
- X ;
- X if (!(op & (APPEND | OVERWRITE)))
- X if (
- X#ifdef MSDOS
- X getftime(f, &tim) ||
- X setftime(t, &tim) ||
- X _chmod(fullrep, 1, ff->fi_attrib) == -1
- X#else
- X stat(pathbuf, &fstat) ||
- X (
- X#ifdef SYSV
- X tim.actime = fstat.st_atime,
- X tim.modtime = fstat.st_mtime,
- X#else
- X tim[0].tv_sec = fstat.st_atime,
- X tim[1].tv_sec = fstat.st_mtime,
- X#endif
- X utimes(fullrep, tim)
- X )
- X#endif
- X )
- X fprintf(stderr, "Strange, couldn't transfer time from %s to %s.\n",
- X pathbuf, fullrep);
- X
- X close(f);
- X close(t);
- X if (k != 0) {
- X if (!(op & APPEND))
- X unlink(fullrep);
- X return(-1);
- X }
- X return(0);
- X}
- X
- X
- X#ifndef RENAME
- Xstatic int rename(from, to)
- X char *from, *to;
- X{
- X if (link(from, to))
- X return(-1);
- X if (unlink(from)) {
- X unlink(to);
- X return(-1);
- X }
- X return(0);
- X}
- X#endif
- X
- X
- Xstatic int myunlink(n, f)
- X char *n;
- X FILEINFO *f;
- X{
- X#ifdef MSDOS
- X int a;
- X
- X if (((a = f->fi_attrib) & FA_RDONLY) && _chmod(n, 1, a & ~FA_RDONLY) < 0) {
- X fprintf(stderr, "Strange, can not _chmod (or unlink) %s.\n", f);
- X return(-1);
- X }
- X#endif
- X if (unlink(n)) {
- X fprintf(stderr, "Strange, can not unlink %s.\n", n);
- X return(-1);
- X }
- X return(0);
- X}
- X
- X
- Xstatic int getreply(m, failact)
- X char *m;
- X int failact;
- X{
- X static FILE *tty = NULL;
- X int c, r;
- X
- X fprintf(stderr, m);
- X if (tty == NULL && (tty = fopen(TTY, "r")) == NULL) {
- X fprintf(stderr, "Can not open %s to get reply.\n", TTY);
- X if (failact == -1)
- X quit();
- X else
- X return(failact);
- X }
- X for (;;) {
- X r = fgetc(tty);
- X if (r == EOF) {
- X fprintf(stderr, "Can not get reply.\n");
- X if (failact == -1)
- X quit();
- X else
- X return(failact);
- X }
- X if (r != '\n')
- X while ((c = fgetc(tty)) != '\n' && c != EOF)
- X ;
- X r = mylower(r);
- X if (r == 'y' || r == 'n')
- X return(r == 'y');
- X fprintf(stderr, "Yes or No? ");
- X }
- X}
- X
- X
- Xstatic void *myalloc(k)
- X unsigned k;
- X{
- X void *ret;
- X
- X if (k == 0)
- X return(NULL);
- X if ((ret = (void *)malloc(k)) == NULL) {
- X fprintf(stderr, "Insufficient memory.\n");
- X quit();
- X }
- X return(ret);
- X}
- X
- X
- Xstatic void *challoc(k, which)
- X int which;
- X int k;
- X{
- X void *ret;
- X CHUNK *p, *q;
- X SLICER *sl = &(slicer[which]);
- X
- X if (k > sl->sl_len) {
- X for (
- X q = NULL, p = freechunks;
- X p != NULL && (sl->sl_len = p->ch_len) < k;
- X q = p, p = p->ch_next
- X )
- X ;
- X if (p == NULL) {
- X sl->sl_len = CHUNKSIZE - sizeof(CHUNK *);
- X p = (CHUNK *)myalloc(CHUNKSIZE);
- X }
- X else if (q == NULL)
- X freechunks = p->ch_next;
- X else
- X q->ch_next = p->ch_next;
- X p->ch_next = sl->sl_first;
- X sl->sl_first = p;
- X sl->sl_unused = (char *)&(p->ch_len);
- X }
- X sl->sl_len -= k;
- X ret = (void *)sl->sl_unused;
- X sl->sl_unused += k;
- X return(ret);
- X}
- X
- X
- Xstatic void chgive(p, k)
- X void *p;
- X unsigned k;
- X{
- X ((CHUNK *)p)->ch_len = k - sizeof(CHUNK *);
- X ((CHUNK *)p)->ch_next = freechunks;
- X freechunks = (CHUNK *)p;
- X}
- X
- X
- X#ifndef MSDOS
- Xstatic void memmove(to, from, k)
- X char *to, *from;
- X unsigned k;
- X{
- X if (from > to)
- X while (k-- != 0)
- X *(to++) = *(from++);
- X else {
- X from += k;
- X to += k;
- X while (k-- != 0)
- X *(--to) = *(--from);
- X }
- X}
- X#endif
- X
- X
- Xstatic int mygetc()
- X{
- X static int lastc = 0;
- X
- X if (lastc == EOF)
- X return(EOF);
- X return(lastc = getchar());
- X}
- X
- X
- X#ifdef MSDOS
- Xstatic int leave()
- X{
- X return(0);
- X}
- X
- Xstatic void cleanup()
- X{
- X int i;
- X
- X if (patch.ph_safeid) {
- X for (i = 0; i < nhandles; i++) {
- X if (!(handles[i]->h_di->di_flags & DI_CLEANED)) {
- X sprintf(pathbuf, "%s%s%03d",
- X handles[i]->h_name, IDF, handles[i]->h_di->di_did);
- X if (unlink(pathbuf))
- X fprintf(stderr, "Strange, couldn't unlink %s.\n", pathbuf);
- X handles[i]->h_di->di_flags |= DI_CLEANED;
- X }
- X }
- X }
- X/*
- X Write device availability: undocumented internal MS-D*S function.
- X Restore previous value.
- X*/
- X bdos(0x37, olddevflag, 3);
- X}
- X
- X#endif
- END_OF_FILE
- if test 7214 -ne `wc -c <'mmv.c.2'`; then
- echo shar: \"'mmv.c.2'\" unpacked with wrong size!
- fi
- # end of 'mmv.c.2'
- fi
- echo shar: End of archive 1 \(of 2\).
- cp /dev/null ark1isdone
- MISSING=""
- for I in 1 2 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked both archives.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
- exit 0 # Just in case...
-